using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using WindowsAPI.Service;

namespace WindowsAPI
{
    [CopyRight("http://www.codeproject.com/Articles/1376/NET-TWAIN-image-scanner")]
    [CopyRight("http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=1376")]
    public class Twain
    {
        private const short CountryUSA = 1;
        private const short LanguageUSA = 13;

        /* ------------------------------------------------------------------------------------------ */

        public static int ScreenBitDepth
        {
            get
            {
                var lv_ScreenDc = GDI32.CreateDC("DISPLAY", null, null, IntPtr.Zero);
                var lv_BitDepth = GDI32.GetDeviceCaps(lv_ScreenDc, 12);
                lv_BitDepth *= GDI32.GetDeviceCaps(lv_ScreenDc, 14);
                GDI32.DeleteDC(lv_ScreenDc);
                return lv_BitDepth;
            }
        }

        /* ------------------------------------------------------------------------------------------ */

        private IntPtr hwnd;
        private TwIdentity appid;
        private TwIdentity srcds;
        private TwEvent evtmsg;
        private WINMSG winmsg;

        /* ------------------------------------------------------------------------------------------ */



        /* ------------------------------------------------------------------------------------------ */

        public Twain()
        {
            appid = new TwIdentity
                        {
                            Id = IntPtr.Zero,
                            Version =
                            {
                                MajorNum = 1,
                                MinorNum = 1,
                                Language = LanguageUSA,
                                Country = CountryUSA,
                                Info = "Hack 1"
                            },
                            ProtocolMajor = TwProtocol.Major,
                            ProtocolMinor = TwProtocol.Minor,
                            SupportedGroups = (int)(TwDG.Image | TwDG.Control),
                            Manufacturer = "NETMaster",
                            ProductFamily = "Freeware",
                            ProductName = "Hack"
                        };


            srcds = new TwIdentity { Id = IntPtr.Zero };

            evtmsg.EventPtr = Marshal.AllocHGlobal(Marshal.SizeOf(winmsg));
        }

        ~Twain() { Marshal.FreeHGlobal(evtmsg.EventPtr); }

        /* ------------------------------------------------------------------------------------------ */

        public void Init(IntPtr hwndp)
        {
            Finish();
            var rc = Twain_32.DSMparent(appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.OpenDSM, ref hwndp);
            if(rc != TwRC.Success) return;

            rc = Twain_32.DSMident(appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.GetDefault, srcds);

            if(rc == TwRC.Success)
                hwnd = hwndp;
            else
                Twain_32.DSMparent(appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.CloseDSM, ref hwndp);
        }

        public void Select()
        {
            CloseSrc();
            if(appid.Id == IntPtr.Zero)
            {
                Init(hwnd);
                if(appid.Id == IntPtr.Zero)
                    return;
            }
            Twain_32.DSMident(appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.UserSelect, srcds);
        }


        public void Acquire()
        {
            CloseSrc();
            if(appid.Id == IntPtr.Zero)
            {
                Init(hwnd);
                if(appid.Id == IntPtr.Zero)
                    return;
            }
            var rc = Twain_32.DSMident(appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.OpenDS, srcds);
            if(rc != TwRC.Success) return;

            var cap = new TwCapability(TwCap.XferCount, 1);
            rc = Twain_32.DScap(appid, srcds, TwDG.Control, TwDAT.Capability, TwMSG.Set, cap);
            if(rc != TwRC.Success)
            {
                CloseSrc();
                return;
            }

            var guif = new TwUserInterface { ShowUI = 1, ModalUI = 1, ParentHand = hwnd };
            rc = Twain_32.DSuserif(appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.EnableDS, guif);
            if(rc != TwRC.Success)
                CloseSrc();
        }


        public ArrayList TransferPictures()
        {
            var pics = new ArrayList();
            if(srcds.Id == IntPtr.Zero) return pics;

            var pxfr = new TwPendingXfers();

            do
            {
                pxfr.Count = 0;
                var hbitmap = IntPtr.Zero;

                var iinf = new TwImageInfo();
                var rc = Twain_32.DSiinf(appid, srcds, TwDG.Image, TwDAT.ImageInfo, TwMSG.Get, iinf);
                if(rc != TwRC.Success)
                {
                    CloseSrc();
                    return pics;
                }

                rc = Twain_32.DSixfer(appid, srcds, TwDG.Image, TwDAT.ImageNativeXfer, TwMSG.Get, ref hbitmap);
                if(rc != TwRC.XferDone)
                {
                    CloseSrc();
                    return pics;
                }

                rc = Twain_32.DSpxfer(appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.EndXfer, pxfr);
                if(rc != TwRC.Success)
                {
                    CloseSrc();
                    return pics;
                }

                pics.Add(hbitmap);
            }
            while(pxfr.Count != 0);

            Twain_32.DSpxfer(appid, srcds, TwDG.Control, TwDAT.PendingXfers, TwMSG.Reset, pxfr);
            return pics;
        }


        public TwainCommand PassMessage(ref Message m)
        {
            if(srcds.Id == IntPtr.Zero)
                return TwainCommand.Not;

            var pos = User32.GetMessagePos();

            winmsg.hwnd = m.HWnd;
            winmsg.message = m.Msg;
            winmsg.wParam = m.WParam;
            winmsg.lParam = m.LParam;
            winmsg.time = User32.GetMessageTime();
            winmsg.x = (short)pos;
            winmsg.y = (short)(pos >> 16);

            Marshal.StructureToPtr(winmsg, evtmsg.EventPtr, false);
            evtmsg.Message = 0;
            var rc = Twain_32.DSevent(appid, srcds, TwDG.Control, TwDAT.Event, TwMSG.ProcessEvent, ref evtmsg);

            if(rc == TwRC.NotDSEvent) return TwainCommand.Not;
            switch((TwMSG)evtmsg.Message)
            {
                case TwMSG.XFerReady: return TwainCommand.TransferReady;
                case TwMSG.CloseDSReq: return TwainCommand.CloseRequest;
                case TwMSG.CloseDSOK: return TwainCommand.CloseOk;
                case TwMSG.DeviceEvent: return TwainCommand.DeviceEvent;
            }

            //if(evtmsg.Message == (short)TwMSG.XFerReady) return TwainCommand.TransferReady;
            //if(evtmsg.Message == (short)TwMSG.CloseDSReq) return TwainCommand.CloseRequest;
            //if(evtmsg.Message == (short)TwMSG.CloseDSOK) return TwainCommand.CloseOk;
            //if(evtmsg.Message == (short)TwMSG.DeviceEvent) return TwainCommand.DeviceEvent;

            return TwainCommand.Null;
        }

        public void CloseSrc()
        {
            if(srcds.Id == IntPtr.Zero) return;
            var guif = new TwUserInterface();
            Twain_32.DSuserif(appid, srcds, TwDG.Control, TwDAT.UserInterface, TwMSG.DisableDS, guif);
            Twain_32.DSMident(appid, IntPtr.Zero, TwDG.Control, TwDAT.Identity, TwMSG.CloseDS, srcds);
        }

        public void Finish()
        {
            CloseSrc();
            if(appid.Id != IntPtr.Zero)
                Twain_32.DSMparent(appid, IntPtr.Zero, TwDG.Control, TwDAT.Parent, TwMSG.CloseDSM, ref hwnd);
            appid.Id = IntPtr.Zero;
        }

        /* ------------------------------------------------------------------------------------------ */
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct WINMSG
    {
        public IntPtr hwnd;
        public int message;
        public IntPtr wParam;
        public IntPtr lParam;
        public int time;
        public int x;
        public int y;
    }
}